#coding:utf8
import os
import hashlib

# 非“/”符结束的目录
root = r'g:/root'
assert os.path.isdir(root) and not root.endswith(r'/'), "非“/”符结束的目录"
TMP = r"g:/root/.tmp"

from kuaipan import kuaiPanApi
loginRes = kuaiPanApi.login('test110@kingsoft.com', '123456')['xLive']
token = loginRes['token']
userId = 765871 #loginRes['userId']
 
def encoding(string):
    if os.name == 'nt':
        return string.decode('gbk').encode('utf8')
    return string

def tolocalpath(string):
    assert not isinstance(string, unicode), 'must encoding by utf8'
    if os.name == 'nt':
        return string.decode('utf8').encode('gbk')
    return string    
        
def make_remote(token, remote, beginParentId = '', path = '/'):
    assert isinstance(remote, RemoteFiles), 'RemoteFiles class request.'
    result = []
    res = kuaiPanApi.dir(token, beginParentId)
    assert res['result'] == 'ok', res
    files = res.get('xLive', {}).get('file', [])
    if isinstance(files, dict):
        files = [files,]
    for item in files:
        obj = {}
        if isinstance(item['name'], unicode):
            item['name'] = item['name'].encode('utf8')
        if item['type'] == 'folder':
            obj['child'] = make_remote(token, remote, item['fileId'], path + item['name'] + r'/')
        obj.update(item)
        obj['vpath'] = path + item['name']
        remote.append(obj)
        result.append(obj)
    return result

def format_local_obj(dirname, vdirname, fname):
    fullfilename = os.path.join(dirname, fname)
    fname = encoding(fname)
    obj = {'type':'folder' if os.path.isdir(fullfilename) else 'file'}
    obj['vpath'] = vdirname + r'/' + fname
    obj['name'] = fname
    obj['modTime'] = os.path.getmtime(fullfilename)
    obj['createdTime'] = os.path.getctime(fullfilename)
    obj['atime'] = os.path.getatime(fullfilename) # 最后访问时间
    obj['size'] = os.path.getsize(fullfilename)
    return obj
    
def make_local(rootPath, local):
    def _callback_dir_func(local, dirname, filenamelist):
        if dirname.startswith('.'):return
        vdirname = encoding(dirname[root.__len__():].replace('\\', '/'))
        if vdirname.startswith(r'/.'):return
        for fname in filenamelist:
            if fname.startswith('.'):continue
            obj = format_local_obj(dirname, vdirname, fname)
            local.append(obj)
    assert isinstance(local, LocalFiles), 'LocalFiles class request.'
    os.path.walk(root, _callback_dir_func, local)
    
class LocalFiles(object):
    @staticmethod
    def cmp(a, b):
        if a['type']  == 'folder' and b['type'] == 'file':
            return -1
        if a['type']  == 'file' and b['type'] == 'folder':
            return 1
        if a['type'] == b['type']:
            if a['vpath'][:a['name'].__len__()].count(r'/') < b['vpath'][:b['name'].__len__()].count(r'/'):
                return 1
            else: return -1
    def __init__(self):
        self.file_dict_vpath = {} # vpath为key，value为 file obj
        self.sorted = False
    def append(self, object):
        assert isinstance(object, dict) and object.has_key('vpath'), object
        self.file_dict_vpath[object['vpath']] = object
        self.sorted = False
    def __setitem__(self, k, v):
        assert isinstance(v, dict) and v.has_key('vpath') and v['vpath'] == k,  v
        self.append(v)
    def __getitem__(self, k):
        return self.get(k)
    def items(self):
        val = self.file_dict_vpath.values()
        if not self.sorted:
            val.sort(LocalFiles.cmp)
            self.sorted = True
        return val
    def get(self, vpath):
        return self.file_dict_vpath.get(vpath)
    def has_vpath(self, vpath):
        return self.file_dict_vpath.has_key(vpath)
    def __str__(self):
        return '\n'.join([d.__str__() for d in self.items()])
    
class RemoteFiles(LocalFiles):
    def __init__(self):
        super(RemoteFiles, self).__init__()
        self.file_dict_fileId = {}  
    def append(self, object):     
        super(RemoteFiles, self).append(object)
        assert object.has_key('fileId'), 'no key fileId: %s' % object
        self.file_dict_fileId[object['fileId']] = object
    def getbyId(self, fileId):
        return self.file_dict_fileId.get(fileId)
    def getParentId(self, obj):
        parentPath = obj['vpath'][:(-1)-obj['name'].__len__()]
        if not parentPath: 
            return ''
        parent = self.get(parentPath)
        if parent:return parent['fileId']
        assert False, obj['vpath']
                  
def up_new(l, remote):
    '''上行新建一个对像（文件/文件夹），成功返回True,否则异常'''
    assert isinstance(l, dict) and l.has_key('vpath'), l
    parentId = remote.getParentId(l)
    if l['type'] =='folder':
        res = kuaiPanApi.newFolder(token, parentId, l['name'])
        assert res['result'] in ('ok', 'targetExist'), res 
        l['fileId'] = res['xLive']['fileId']
        remote.append(l)
    else:
        fp = open(tolocalpath(root + l['vpath']), 'rb')
        contents = fp.read()
        fp.close()
        res = kuaiPanApi.newFile(token, parentId, l['name'], contents)
        assert res['result'] in ('ok', 'targetExist'), res
        res.pop('result'), res.update(l), l.update(res) 
        remote.append(res)
        del contents
    return True
############################
# 在关闭程序时:删除的文件，会再次下载。
#             新增的文件会上传.
#             修改的文件会XXOO
############################
def sync_up(local, remote):
    '''远程服务器没有的，直接新建上传.
    @return: 返回远程与本地同时有此文件的list，注意是文件
    '''
    result = []
    for l in local.items():
        if remote.has_vpath(l['vpath']): 
            if l['type'] == 'file': 
                result.append({'local':l, 'remote': remote.get(l['vpath'])})
            continue
        else:
            #远程服务器没有，文件夹新建，文件则上传
            up_new(l, remote)
    return result

def _write2local(fileId, wfullPath):
    urlfp = kuaiPanApi.down(token, fileId)
    assert urlfp, urlfp
    fp = open(wfullPath, 'wb')
    while True:
        contents = urlfp.read(4098)
        if not contents:break
        fp.write(contents)
    fp.close()
    
def sync_down(local, remote):
    result = []
    for m in remote.items():
        if local.has_vpath(m['vpath']):
            if m['type'] =='file': 
                result.append({'local':local.get(m['vpath']), 'remote':m })
            continue
        else:
            # 本地没有的，下载下来
            fullpath = tolocalpath(root + m['vpath'])
            if m['type'] == 'folder':
                try:
                    print 'mkdir', fullpath
                    os.mkdir(fullpath)
                except Exception, e: print e
            else:
                print 'downing:', fullpath
                _write2local(m['fileId'], fullpath)
            local.append(m)
    return result

def _localsha1(vpath):
    fp = open(tolocalpath(root + vpath), 'rb')
    contents = fp.read()
    fp.close()
    return hashlib.sha1(contents).hexdigest()
    
def sync_same(local, remote, sameList):
    '''路径名相同的文件直接比较sha1'''
    for item in sameList:
        l, m = item['local'], item['remote']
        assert l['type'] == m['type'] == 'file' and l['vpath'] == m['vpath'], 'not in same path and name %s , %s' % (l, m)
        if not l.has_key('sha1'):  
            l['sha1'] = _localsha1(l['vpath'])
        if l['sha1'].lower() == m['sha1'].lower():
            l['fileVer'] = m['fileVer']
            l['fileId'] = m['fileId']
            local.append(l) 
            continue 
        else:
            #这里处理冲突
            print 'sha1 not the same.', l['vpath']
            lfileVer = l.get('fileVer', -1)
            if lfileVer < int(m['fileVer']): 
                # 1).远程文件版本高，覆盖本地
                fullpath = tolocalpath(root + m['vpath'])
                _write2local(m['fileId'], fullpath)

    